/*
 * Author: vdaras
 */

#include "EngineCore.h"

#include "EventProcessor.h"
#include "GlobalTimer.h"
#include "global_script_functions.h"
#include "Globals.h"
#include "KeyMapper.h"
#include "ResourceManager.h"
#include "ServiceLocator.h"
#include "State.h"
#include "StateStack.h"
#include <SDL/SDL.h>
#include "ValueRepository.h"

const int TEST_CAM_BORDER_WIDTH = 10 * 1024;
const int TEST_CAM_BORDER_HEIGHT = 4 * 1024;


#include "gui/Button.h"
#include "gui/ImageButton.h"
#include "gui/Picture.h"

EngineCore::EngineCore()
:
m_camera(NULL),
m_gTimer(NULL),
m_resourceManager(NULL)
{
    m_keyMapper = new KeyMapper;
    m_stateStack = new StateStack;
    m_eventProcessor = new EventProcessor;
    m_valueRepository = new ValueRepository;
}


EngineCore::~EngineCore()
{
    delete m_stateStack;
    delete m_eventProcessor;
    delete m_camera;
    delete m_gTimer;
    delete m_keyMapper;
    delete m_resourceManager;
    delete m_valueRepository;
    //delete m_scriptManager;
}


/**
 * This method initializes the engine. The sdl::Application::Init method is
 * called first in order to initialize the graphics core and the gui's
 * screen.
 *
 * @param screenW - screen width
 * @param screenH - screen height
 * @param bpp - bits per pixel
 * @param fullscreen - flag that indicated if the application is fullscreen.
 * @return a boolean value indicating if initialization was successful or
 *         not.
 */

bool EngineCore::Init(int screenW, int screenH, int bpp, bool fullscreen)
{
    if( !sdl::Application::Init( screenW, screenH, bpp, fullscreen ) )
    {
        return false;
    }

    Globals *globals = Globals::GetInstance( );

    m_camera = new Camera( 0, 0, globals->GetScreenWidth( ), globals->GetScreenHeight( ) );

    m_camera->SetBorders( TEST_CAM_BORDER_WIDTH, TEST_CAM_BORDER_HEIGHT );

    m_gTimer = GlobalTimer::GetInstance( );

    m_resourceManager = ResourceManager::GetInstance( );

    m_resourceManager->InitializePools( );

    ServiceLocator< Camera >::SetService( m_camera );
    ServiceLocator< ValueRepository >::SetService( m_valueRepository );
    ServiceLocator< KeyMapper >::SetService( m_keyMapper );
    ServiceLocator< StateStack >::SetService( m_stateStack );
    ServiceLocator< EngineCore >::SetService( this );

    return true;
}


/**
 * This method is the central control point of the engine. While the engine
 * runs, execution is located inside this method. A brief overview of Run:
 *
 * There is a loop running as long as the m_running flag is enabled. If the
 * flag is set to false then the application exits. A whole loop marks a
 * frame. At the start of the frame we get the top of the  State stack in
 * order to determine the current State of the engine (get a look at the
 * State design pattern). There is a polling loop for events, and if an
 * event occurrs it gets forwarded to the current State. Then the overriden
 * methods that Update and Render the current State are called and after
 * that there is a frame rate capping segment of code. At the end of the
 * frame the garbage collectors for Script Objects and Resources are called.
 *
 * @return an exit code
 */

int EngineCore::Run()
{
    int exitCode = 0;    

    PushState( "Resources/Scripts/Lua/States/TestState.lua" );
    
    if( m_stateStack->Size( ) == 0 )
    {
        return 1;
    }

    State *currentState = m_stateStack->Top( );

    SDL_Event evt;

    Event *event;
    
    TimedUpdater garbageCTimer;
    
    while( GetRunning( ) )
    {
        m_gTimer->MarkFrameStart( );

        currentState = m_stateStack->Top( );

        m_eventProcessor->DisposeEvents();
	
        while( SDL_PollEvent( &evt ) )
        {
            if( evt.type == SDL_QUIT )
            {
                Exit( );
                exitCode = 0;
                break;
            }
            else
            {
				event = m_eventProcessor->Process( evt );
				
				if( event )
				{
					MouseEvent *mevt = dynamic_cast< MouseEvent* >( event );
					
					KeyboardEvent *kevt = dynamic_cast< KeyboardEvent* >( event );
					
					if( mevt )
					{
					m_gScreen->OnMouseEvent( *mevt );
					
					currentState->OnMouseEvent( *mevt );
					
					}
					else if( kevt )
					{
						currentState->OnKeyboardEvent( *kevt );
					}
				}
            }
        }

        currentState->Update( m_gTimer->GetDeltaTime( ) );

        this->Render();

		if( m_stateStack->MustPop ( ) )
		{
			m_stateStack->Pop( );
		}
	
        if(garbageCTimer.Update(1000))
        {
            m_resourceManager->GarbageCollect( );
        }
    }
    

    return exitCode;
}


/**
 * @return A pointer to the Engine Core's Camera.
 */

Camera *EngineCore::GetCamera()
{
    return m_camera;
}


/**
 * Renders State and gui.
 */

void EngineCore::Render()
{
    m_gCore->BeginRendering( );

    m_stateStack->Top( )->Render( m_gCore );
    
    glLoadIdentity( );

    m_gScreen->Draw( );

    m_gCore->EndRendering( );
}
